/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * Main.java
 * Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui;

import weka.classifiers.bayes.net.GUI;
import weka.classifiers.evaluation.ThresholdCurve;
import weka.core.Copyright;
import weka.core.Instances;
import weka.core.Memory;
import weka.core.Option;
import weka.core.OptionHandler;
import weka.core.SelectedTag;
import weka.core.SystemInfo;
import weka.core.Tag;
import weka.core.Utils;
import weka.core.Version;
import weka.gui.arffviewer.ArffViewerMainPanel;
import weka.gui.beans.KnowledgeFlowApp;
import weka.gui.beans.StartUpListener;
import weka.gui.boundaryvisualizer.BoundaryVisualizer;
import weka.gui.experiment.Experimenter;
import weka.gui.explorer.Explorer;
import weka.gui.graphvisualizer.GraphVisualizer;
import weka.gui.sql.SqlViewer;
import weka.gui.treevisualizer.Node;
import weka.gui.treevisualizer.NodePlace;
import weka.gui.treevisualizer.PlaceNode2;
import weka.gui.treevisualizer.TreeBuild;
import weka.gui.treevisualizer.TreeVisualizer;
import weka.gui.visualize.PlotData2D;
import weka.gui.visualize.ThresholdVisualizePanel;
import weka.gui.visualize.VisualizePanel;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.Reader;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;

/**
 * Menu-based GUI for Weka, replacement for the GUIChooser.
 * 
 * <!-- options-start --> Valid options are: <p/>
 * 
 * <pre>
 *  -gui &lt;MDI|SDI&gt;
 *  Determines the layout of the GUI:
 *  MDI = MDI Layout
 *  SDI = SDI Layout
 *  (default: MDI)
 * </pre>
 * 
 * <!-- options-end -->
 * 
 * @author fracpete (fracpete at waikato dot ac dot nz)
 * @version $Revision: 4718 $
 */
public class Main extends JFrame implements OptionHandler {

	/** for serialization. */
	private static final long serialVersionUID = 1453813254824253849L;

	/**
	 * DesktopPane with background image.
	 * 
	 * @author fracpete (fracpete at waikato dot ac dot nz)
	 * @version $Revision: 4718 $
	 */
	public static class BackgroundDesktopPane extends JDesktopPane {

		/** for serialization. */
		private static final long serialVersionUID = 2046713123452402745L;

		/** the actual background image. */
		protected Image m_Background;

		/**
		 * intializes the desktop pane.
		 * 
		 * @param image
		 *            the image to use as background
		 */
		public BackgroundDesktopPane(String image) {
			super();

			try {
				m_Background = Toolkit.getDefaultToolkit().getImage(
						ClassLoader.getSystemResource(image));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		/**
		 * draws the background image.
		 * 
		 * @param g
		 *            the graphics context
		 */
		public void paintComponent(Graphics g) {
			super.paintComponent(g);

			if (m_Background != null) {
				g.setColor(Color.WHITE);
				g.clearRect(0, 0, getWidth(), getHeight());

				int width = m_Background.getWidth(null);
				int height = m_Background.getHeight(null);
				int x = (getWidth() - width) / 2;
				int y = (getHeight() - height) / 2;
				g.drawImage(m_Background, x, y, width, height, this);
			}
		}
	}

	/**
	 * Specialized JFrame class.
	 * 
	 * @author fracpete (fracpete at waikato dot ac dot nz)
	 * @version $Revision: 4718 $
	 */
	public static class ChildFrameSDI extends JFrame {

		/** for serialization. */
		private static final long serialVersionUID = 8588293938686425618L;

		/** the parent frame. */
		protected Main m_Parent;

		/**
		 * constructs a new internal frame that knows about its parent.
		 * 
		 * @param parent
		 *            the parent frame
		 * @param title
		 *            the title of the frame
		 */
		public ChildFrameSDI(Main parent, String title) {
			super(title);

			m_Parent = parent;

			addWindowListener(new WindowAdapter() {
				public void windowActivated(WindowEvent e) {
					// update title of parent
					if (getParentFrame() != null)
						getParentFrame().createTitle(getTitle());
				}
			});

			// add to parent
			if (getParentFrame() != null) {
				getParentFrame().addChildFrame(this);
				setIconImage(getParentFrame().getIconImage());
			}
		}

		/**
		 * returns the parent frame, can be null.
		 * 
		 * @return the parent frame
		 */
		public Main getParentFrame() {
			return m_Parent;
		}

		/**
		 * de-registers the child frame with the parent first.
		 */
		public void dispose() {
			if (getParentFrame() != null) {
				getParentFrame().removeChildFrame(this);
				getParentFrame().createTitle("");
			}

			super.dispose();
		}
	}

	/**
	 * Specialized JInternalFrame class.
	 * 
	 * @author fracpete (fracpete at waikato dot ac dot nz)
	 * @version $Revision: 4718 $
	 */
	public static class ChildFrameMDI extends JInternalFrame {

		/** for serialization. */
		private static final long serialVersionUID = 3772573515346899959L;

		/** the parent frame. */
		protected Main m_Parent;

		/**
		 * constructs a new internal frame that knows about its parent.
		 * 
		 * @param parent
		 *            the parent frame
		 * @param title
		 *            the title of the frame
		 */
		public ChildFrameMDI(Main parent, String title) {
			super(title, true, true, true, true);

			m_Parent = parent;

			addInternalFrameListener(new InternalFrameAdapter() {
				public void internalFrameActivated(InternalFrameEvent e) {
					// update title of parent
					if (getParentFrame() != null)
						getParentFrame().createTitle(getTitle());
				}
			});

			// add to parent
			if (getParentFrame() != null) {
				getParentFrame().addChildFrame(this);
				getParentFrame().jDesktopPane.add(this);
			}
		}

		/**
		 * returns the parent frame, can be null.
		 * 
		 * @return the parent frame
		 */
		public Main getParentFrame() {
			return m_Parent;
		}

		/**
		 * de-registers the child frame with the parent first.
		 */
		public void dispose() {
			if (getParentFrame() != null) {
				getParentFrame().removeChildFrame(this);
				getParentFrame().createTitle("");
			}

			super.dispose();
		}
	}

	/** displays the GUI as MDI. */
	public final static int GUI_MDI = 0;

	/** displays the GUI as SDI. */
	public final static int GUI_SDI = 1;

	/** GUI tags. */
	public static final Tag[] TAGS_GUI = {
			new Tag(GUI_MDI, "MDI", "MDI Layout"),
			new Tag(GUI_SDI, "SDI", "SDI Layout") };

	/** the frame itself. */
	protected Main m_Self;

	/** the type of GUI to display. */
	protected int m_GUIType = GUI_MDI;

	/**
	 * variable for the Main class which would be set to null by the memory
	 * monitoring thread to free up some memory if we running out of memory.
	 */
	protected static Main m_MainCommandline;

	/** singleton instance of the GUI. */
	protected static Main m_MainSingleton;

	/**
	 * list of things to be notified when the startup process of the
	 * KnowledgeFlow is complete.
	 */
	protected static Vector m_StartupListeners = new Vector();

	/** for monitoring the Memory consumption. */
	protected static Memory m_Memory = new Memory(true);

	/** contains the child frames (title &lt;-&gt; object). */
	protected HashSet<Container> m_ChildFrames = new HashSet<Container>();

	/** The frame of the LogWindow. */
	protected static LogWindow m_LogWindow = new LogWindow();

	/** filechooser for the TreeVisualizer. */
	protected JFileChooser m_FileChooserTreeVisualizer = new JFileChooser(
			new File(System.getProperty("user.dir")));

	/** filechooser for the GraphVisualizer. */
	protected JFileChooser m_FileChooserGraphVisualizer = new JFileChooser(
			new File(System.getProperty("user.dir")));

	/** filechooser for Plots. */
	protected JFileChooser m_FileChooserPlot = new JFileChooser(new File(System
			.getProperty("user.dir")));

	/** filechooser for ROC curves. */
	protected JFileChooser m_FileChooserROC = new JFileChooser(new File(System
			.getProperty("user.dir")));

	// GUI components
	private JMenu jMenuHelp;

	private JMenu jMenuVisualization;

	private JMenu jMenuTools;

	private JDesktopPane jDesktopPane;

	private JMenu jMenuApplications;

	private JMenuItem jMenuItemHelpSystemInfo;

	private JMenuItem jMenuItemHelpAbout;

	private JMenuItem jMenuItemHelpHomepage;

	private JMenuItem jMenuItemHelpWekaWiki;

	private JMenuItem jMenuItemHelpWekaDoc;

	private JMenuItem jMenuItemHelpSourceforge;

	private JMenuItem jMenuItemVisualizationBoundaryVisualizer;

	private JMenuItem jMenuItemVisualizationGraphVisualizer;

	private JMenuItem jMenuItemVisualizationTreeVisualizer;

	private JMenuItem jMenuItemVisualizationROC;

	private JMenuItem jMenuItemVisualizationPlot;

	private JMenuItem jMenuItemToolsEnsembleLibrary;

	private JMenuItem jMenuItemToolsSqlViewer;

	private JMenuItem jMenuItemToolsArffViewer;

	private JMenuItem jMenuItemApplicationsSimpleCLI;

	private JMenuItem jMenuItemApplicationsKnowledgeFlow;

	private JMenuItem jMenuItemApplicationsExperimenter;

	private JMenuItem jMenuItemApplicationsExplorer;

	private JMenuItem jMenuItemProgramExit;

	private JMenuItem jMenuItemProgramLogWindow;

	private JMenuItem jMenuItemProgramMemoryUsage;

	private JMenuItem jMenuItemProgramPreferences; // TODO: see below

	private JMenu jMenuProgram;

	private JMenu jMenuExtensions;

	private JMenu jMenuWindows;

	private JMenuBar jMenuBar;

	/**
	 * default constructor.
	 */
	public Main() {
		super();
	}

	/**
	 * creates a frame (depending on m_GUIType) and returns it.
	 * 
	 * @param parent
	 *            the parent of the generated frame
	 * @param title
	 *            the title of the frame
	 * @param c
	 *            the component to place, can be null
	 * @param layout
	 *            the layout to use, e.g., BorderLayout
	 * @param layoutConstraints
	 *            the layout constraints, e.g., BorderLayout.CENTER
	 * @param width
	 *            the width of the frame, ignored if -1
	 * @param height
	 *            the height of the frame, ignored if -1
	 * @param menu
	 *            an optional menu
	 * @param listener
	 *            if true a default listener is added
	 * @param visible
	 *            if true then the frame is made visible immediately
	 * @return the generated frame
	 * @see #m_GUIType
	 */
	protected Container createFrame(Main parent, String title, Component c,
			LayoutManager layout, Object layoutConstraints, int width,
			int height, JMenuBar menu, boolean listener, boolean visible) {

		Container result = null;

		if (m_GUIType == GUI_MDI) {
			final ChildFrameMDI frame = new ChildFrameMDI(parent, title);

			// layout
			frame.setLayout(layout);
			if (c != null)
				frame.getContentPane().add(c, layoutConstraints);

			// menu
			frame.setJMenuBar(menu);

			// size
			frame.pack();
			if ((width > -1) && (height > -1))
				frame.setSize(width, height);
			frame.validate();

			// listener?
			if (listener) {
				frame.addInternalFrameListener(new InternalFrameAdapter() {
					public void internalFrameClosing(InternalFrameEvent e) {
						frame.dispose();
					}
				});
			}

			// display frame
			if (visible) {
				frame.setVisible(true);
				try {
					frame.setSelected(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			result = frame;
		} else if (m_GUIType == GUI_SDI) {
			final ChildFrameSDI frame = new ChildFrameSDI(parent, title);

			// layout
			frame.setLayout(layout);
			if (c != null)
				frame.getContentPane().add(c, layoutConstraints);

			// menu
			frame.setJMenuBar(menu);

			// size
			frame.pack();
			if ((width > -1) && (height > -1))
				frame.setSize(width, height);
			frame.validate();

			// location
			int screenHeight = getGraphicsConfiguration().getBounds().height;
			int screenWidth = getGraphicsConfiguration().getBounds().width;
			frame.setLocation((screenWidth - frame.getBounds().width) / 2,
					(screenHeight - frame.getBounds().height) / 2);

			// listener?
			if (listener) {
				frame.addWindowListener(new WindowAdapter() {
					public void windowClosing(WindowEvent e) {
						frame.dispose();
					}
				});
			}

			// display frame
			if (visible)
				frame.setVisible(true);

			result = frame;
		}

		return result;
	}

	/**
	 * insert the menu item in a sorted fashion.
	 * 
	 * @param menu
	 *            the menu to add the item to
	 * @param menuitem
	 *            the menu item to add
	 */
	protected void insertMenuItem(JMenu menu, JMenuItem menuitem) {
		insertMenuItem(menu, menuitem, 0);
	}

	/**
	 * insert the menu item in a sorted fashion.
	 * 
	 * @param menu
	 *            the menu to add the item to
	 * @param menuitem
	 *            the menu item to add
	 * @param startIndex
	 *            the index in the menu to start with (0-based)
	 */
	protected void insertMenuItem(JMenu menu, JMenuItem menuitem, int startIndex) {
		boolean inserted;
		int i;
		JMenuItem current;
		String currentStr;
		String newStr;

		inserted = false;
		newStr = menuitem.getText().toLowerCase();

		// try to find a spot inbetween
		for (i = startIndex; i < menu.getMenuComponentCount(); i++) {
			if (!(menu.getMenuComponent(i) instanceof JMenuItem))
				continue;

			current = (JMenuItem) menu.getMenuComponent(i);
			currentStr = current.getText().toLowerCase();
			if (currentStr.compareTo(newStr) > 0) {
				inserted = true;
				menu.insert(menuitem, i);
				break;
			}
		}

		// add it at the end if not yet inserted
		if (!inserted)
			menu.add(menuitem);
	}

	/**
	 * initializes the GUI.
	 */
	protected void initGUI() {
		m_Self = this;

		try {
			// main window
			createTitle("");
			this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
			this.setIconImage(new ImageIcon(getClass().getClassLoader()
					.getResource("weka/gui/weka_icon.gif")).getImage());

			// bits and pieces
			m_FileChooserGraphVisualizer
					.addChoosableFileFilter(new ExtensionFileFilter(".bif",
							"BIF Files (*.bif)"));
			m_FileChooserGraphVisualizer
					.addChoosableFileFilter(new ExtensionFileFilter(".xml",
							"XML Files (*.xml)"));

			m_FileChooserPlot.addChoosableFileFilter(new ExtensionFileFilter(
					Instances.FILE_EXTENSION, "ARFF Files (*"
							+ Instances.FILE_EXTENSION + ")"));
			m_FileChooserPlot.setMultiSelectionEnabled(true);

			m_FileChooserROC.addChoosableFileFilter(new ExtensionFileFilter(
					Instances.FILE_EXTENSION, "ARFF Files (*"
							+ Instances.FILE_EXTENSION + ")"));

			// Desktop
			if (m_GUIType == GUI_MDI) {
				jDesktopPane = new BackgroundDesktopPane(
						"weka/gui/images/weka_background.gif");
				jDesktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
				setContentPane(jDesktopPane);
			} else {
				jDesktopPane = null;
			}

			// Menu
			jMenuBar = new JMenuBar();
			setJMenuBar(jMenuBar);

			// Program
			jMenuProgram = new JMenu();
			jMenuBar.add(jMenuProgram);
			jMenuProgram.setText("Program");
			jMenuProgram.setMnemonic('P');

			// Program/Preferences
			// TODO: read all properties from all props file and display them
			/*
			 * jMenuItemProgramPreferences = new JMenuItem();
			 * jMenuProgram.add(jMenuItemProgramPreferences);
			 * jMenuItemProgramPreferences.setText("Preferences");
			 * jMenuItemProgramPreferences.setMnemonic('P');
			 * jMenuItemProgramPreferences.addActionListener(new
			 * ActionListener() { public void actionPerformed(ActionEvent evt) {
			 * System.out.println("jMenuItemProgramPreferences.actionPerformed,
			 * event="+evt); //TODO add your code for
			 * jMenuItemProgramPreferences.actionPerformed } });
			 */

			// Program/LogWindow
			jMenuItemProgramLogWindow = new JMenuItem();
			jMenuProgram.add(jMenuItemProgramLogWindow);
			jMenuItemProgramLogWindow.setText("LogWindow");
			jMenuItemProgramLogWindow.setMnemonic('L');
			jMenuItemProgramLogWindow.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					m_LogWindow.setVisible(true);
				}
			});

			jMenuItemProgramMemoryUsage = new JMenuItem();
			jMenuProgram.add(jMenuItemProgramMemoryUsage);
			jMenuItemProgramMemoryUsage.setText("Memory usage");
			jMenuItemProgramMemoryUsage.setMnemonic('M');
			jMenuItemProgramMemoryUsage.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					String title = jMenuItemProgramMemoryUsage.getText();
					if (!containsWindow(title)) {
						final MemoryUsagePanel panel = new MemoryUsagePanel();
						Container c = createFrame(m_Self, title, panel,
								new BorderLayout(), BorderLayout.CENTER, 400,
								50, null, true, true);

						// optimize size
						Dimension size = c.getPreferredSize();
						c.setSize(new Dimension((int) size.getWidth(),
								(int) size.getHeight()));

						// stop threads
						if (m_GUIType == GUI_MDI) {
							final ChildFrameMDI frame = (ChildFrameMDI) c;
							Point l = panel.getFrameLocation();
							if ((l.x != -1) && (l.y != -1))
								frame.setLocation(l);
							frame
									.addInternalFrameListener(new InternalFrameAdapter() {
										public void internalFrameClosing(
												InternalFrameEvent e) {
											panel.stopMonitoring();
										}
									});
						} else {
							final ChildFrameSDI frame = (ChildFrameSDI) c;
							Point l = panel.getFrameLocation();
							if ((l.x != -1) && (l.y != -1))
								frame.setLocation(l);
							frame.addWindowListener(new WindowAdapter() {
								public void windowClosing(WindowEvent e) {
									panel.stopMonitoring();
								}
							});
						}
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			jMenuProgram.add(new JSeparator());

			// Program/Exit
			jMenuItemProgramExit = new JMenuItem();
			jMenuProgram.add(jMenuItemProgramExit);
			jMenuItemProgramExit.setText("Exit");
			jMenuItemProgramExit.setMnemonic('E');
			jMenuItemProgramExit.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					// close all children
					Iterator iter = getWindowList();
					Vector<Container> list = new Vector<Container>();
					while (iter.hasNext())
						list.add((Container) iter.next());
					for (int i = 0; i < list.size(); i++) {
						Container c = list.get(i);
						if (c instanceof ChildFrameMDI)
							((ChildFrameMDI) c).dispose();
						else if (c instanceof ChildFrameSDI)
							((ChildFrameSDI) c).dispose();
					}
					// close logwindow
					m_LogWindow.dispose();
					// close main window
					m_Self.dispose();
					// make sure we stop
					System.exit(0);
				}
			});

			// Applications
			jMenuApplications = new JMenu();
			jMenuBar.add(jMenuApplications);
			jMenuApplications.setText("Applications");
			jMenuApplications.setMnemonic('A');

			// Applications/Explorer
			jMenuItemApplicationsExplorer = new JMenuItem();
			jMenuApplications.add(jMenuItemApplicationsExplorer);
			jMenuItemApplicationsExplorer.setText("Explorer");
			jMenuItemApplicationsExplorer.setMnemonic('E');
			jMenuItemApplicationsExplorer
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							String title = jMenuItemApplicationsExplorer
									.getText();
							if (!containsWindow(title)) {
								createFrame(m_Self, title, new Explorer(),
										new BorderLayout(),
										BorderLayout.CENTER, 800, 600, null,
										true, true);
							} else {
								showWindow(getWindow(title));
							}
						}
					});

			// Applications/Experimenter
			jMenuItemApplicationsExperimenter = new JMenuItem();
			jMenuApplications.add(jMenuItemApplicationsExperimenter);
			jMenuItemApplicationsExperimenter.setText("Experimenter");
			jMenuItemApplicationsExperimenter.setMnemonic('X');
			jMenuItemApplicationsExperimenter
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							String title = jMenuItemApplicationsExperimenter
									.getText();
							if (!containsWindow(title)) {
								createFrame(m_Self, title, new Experimenter(
										false), new BorderLayout(),
										BorderLayout.CENTER, 800, 600, null,
										true, true);
							} else {
								showWindow(getWindow(title));
							}
						}
					});

			// Applications/KnowledgeFlow
			jMenuItemApplicationsKnowledgeFlow = new JMenuItem();
			jMenuApplications.add(jMenuItemApplicationsKnowledgeFlow);
			jMenuItemApplicationsKnowledgeFlow.setText("KnowledgeFlow");
			jMenuItemApplicationsKnowledgeFlow.setMnemonic('K');
			jMenuItemApplicationsKnowledgeFlow
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							String title = jMenuItemApplicationsKnowledgeFlow
									.getText();
							if (!containsWindow(title)) {
								KnowledgeFlowApp.createSingleton(new String[0]);
								createFrame(m_Self, title, KnowledgeFlowApp
										.getSingleton(), new BorderLayout(),
										BorderLayout.CENTER, 900, 600, null,
										true, true);
							} else {
								showWindow(getWindow(title));
							}
						}
					});

			// Applications/SimpleCLI
			jMenuItemApplicationsSimpleCLI = new JMenuItem();
			jMenuApplications.add(jMenuItemApplicationsSimpleCLI);
			jMenuItemApplicationsSimpleCLI.setText("SimpleCLI");
			jMenuItemApplicationsSimpleCLI.setMnemonic('S');
			jMenuItemApplicationsSimpleCLI
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							String title = jMenuItemApplicationsSimpleCLI
									.getText();
							if (!containsWindow(title)) {
								try {
									createFrame(m_Self, title,
											new SimpleCLIPanel(),
											new BorderLayout(),
											BorderLayout.CENTER, 600, 500,
											null, true, true);
								} catch (Exception e) {
									e.printStackTrace();
									JOptionPane.showMessageDialog(m_Self,
											"Error instantiating SimpleCLI:\n"
													+ e.getMessage());
									return;
								}
							} else {
								showWindow(getWindow(title));
							}
						}
					});

			// Tools
			jMenuTools = new JMenu();
			jMenuBar.add(jMenuTools);
			jMenuTools.setText("Tools");
			jMenuTools.setMnemonic('T');

			// Tools/ArffViewer
			jMenuItemToolsArffViewer = new JMenuItem();
			jMenuTools.add(jMenuItemToolsArffViewer);
			jMenuItemToolsArffViewer.setText("ArffViewer");
			jMenuItemToolsArffViewer.setMnemonic('A');
			jMenuItemToolsArffViewer.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					String title = jMenuItemToolsArffViewer.getText();
					if (!containsWindow(title)) {
						ArffViewerMainPanel panel = new ArffViewerMainPanel(
								null);
						panel.setConfirmExit(false);
						Container frame = createFrame(m_Self, title, panel,
								new BorderLayout(), BorderLayout.CENTER, 800,
								600, panel.getMenu(), true, true);
						panel.setParent(frame);
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			// Tools/SqlViewer
			jMenuItemToolsSqlViewer = new JMenuItem();
			jMenuTools.add(jMenuItemToolsSqlViewer);
			jMenuItemToolsSqlViewer.setText("SqlViewer");
			jMenuItemToolsSqlViewer.setMnemonic('S');
			jMenuItemToolsSqlViewer.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					String title = jMenuItemToolsSqlViewer.getText();
					if (!containsWindow(title)) {
						final SqlViewer sql = new SqlViewer(null);
						final Container frame = createFrame(m_Self, title, sql,
								new BorderLayout(), BorderLayout.CENTER, -1,
								-1, null, false, true);

						// custom listener
						if (frame instanceof ChildFrameMDI) {
							((ChildFrameMDI) frame)
									.addInternalFrameListener(new InternalFrameAdapter() {
										public void internalFrameClosing(
												InternalFrameEvent e) {
											sql.saveSize();
											((ChildFrameMDI) frame).dispose();
										}
									});
						} else if (frame instanceof ChildFrameSDI) {
							((ChildFrameSDI) frame)
									.addWindowListener(new WindowAdapter() {
										public void windowClosing(WindowEvent e) {
											sql.saveSize();
											((ChildFrameSDI) frame).dispose();
										}
									});
						}
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			// Tools/Bayes net editor
			// Tools/Bayes net editor
			final JMenuItem jMenuItemBayesNet = new JMenuItem();
			jMenuTools.add(jMenuItemBayesNet);
			jMenuItemBayesNet.setText("Bayes net editor");
			jMenuItemBayesNet.setMnemonic('N');

			jMenuItemBayesNet.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					String title = jMenuItemBayesNet.getText();

					if (!containsWindow(title)) {
						final GUI bayesNetGUI = new GUI();
						final Container frame = createFrame(m_Self, title,
								bayesNetGUI, new BorderLayout(),
								BorderLayout.CENTER, 800, 600, bayesNetGUI
										.getMenuBar(), false, true);
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			// Tools/EnsembleLibrary
			/*
			 * currently disabled due to bugs... FracPete
			 * jMenuItemToolsEnsembleLibrary = new JMenuItem();
			 * jMenuTools.add(jMenuItemToolsEnsembleLibrary);
			 * jMenuItemToolsEnsembleLibrary.setText("EnsembleLibrary");
			 * jMenuItemToolsEnsembleLibrary.setMnemonic('E');
			 * jMenuItemToolsEnsembleLibrary.addActionListener(new
			 * ActionListener() { public void actionPerformed(ActionEvent evt) {
			 * String title = jMenuItemToolsEnsembleLibrary.getText(); if
			 * (!containsWindow(title)) { EnsembleLibrary value = new
			 * EnsembleLibrary(); EnsembleLibraryEditor libraryEditor = new
			 * EnsembleLibraryEditor(); libraryEditor.setValue(value);
			 * createFrame( m_Self, title, libraryEditor.getCustomEditor(), new
			 * BorderLayout(), BorderLayout.CENTER, 800, 600, null, true, true); }
			 * else { showWindow(getWindow(title)); } } });
			 */

			// Visualization
			jMenuVisualization = new JMenu();
			jMenuBar.add(jMenuVisualization);
			jMenuVisualization.setText("Visualization");
			jMenuVisualization.setMnemonic('V');

			// Visualization/Plot
			jMenuItemVisualizationPlot = new JMenuItem();
			jMenuVisualization.add(jMenuItemVisualizationPlot);
			jMenuItemVisualizationPlot.setText("Plot");
			jMenuItemVisualizationPlot.setMnemonic('P');
			jMenuItemVisualizationPlot.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					// choose file
					int retVal = m_FileChooserPlot.showOpenDialog(m_Self);
					if (retVal != JFileChooser.APPROVE_OPTION)
						return;

					// build plot
					VisualizePanel panel = new VisualizePanel();
					String filenames = "";
					File[] files = m_FileChooserPlot.getSelectedFiles();
					for (int j = 0; j < files.length; j++) {
						String filename = files[j].getAbsolutePath();
						if (j > 0)
							filenames += ", ";
						filenames += filename;
						System.err
								.println("Loading instances from " + filename);
						try {
							Reader r = new java.io.BufferedReader(
									new FileReader(filename));
							Instances i = new Instances(r);
							i.setClassIndex(i.numAttributes() - 1);
							PlotData2D pd1 = new PlotData2D(i);

							if (j == 0) {
								pd1.setPlotName("Master plot");
								panel.setMasterPlot(pd1);
							} else {
								pd1.setPlotName("Plot " + (j + 1));
								pd1.m_useCustomColour = true;
								pd1.m_customColour = (j % 2 == 0) ? Color.red
										: Color.blue;
								panel.addPlot(pd1);
							}
						} catch (Exception e) {
							e.printStackTrace();
							JOptionPane.showMessageDialog(m_Self,
									"Error loading file '" + files[j] + "':\n"
											+ e.getMessage());
							return;
						}
					}

					// create frame
					createFrame(m_Self, jMenuItemVisualizationPlot.getText()
							+ " - " + filenames, panel, new BorderLayout(),
							BorderLayout.CENTER, 800, 600, null, true, true);
				}
			});

			// Visualization/ROC
			// based on this Wiki article:
			// http://weka.sourceforge.net/wiki/index.php/Visualizing_ROC_curve
			jMenuItemVisualizationROC = new JMenuItem();
			jMenuVisualization.add(jMenuItemVisualizationROC);
			jMenuItemVisualizationROC.setText("ROC");
			jMenuItemVisualizationROC.setMnemonic('R');
			jMenuItemVisualizationROC.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					// choose file
					int retVal = m_FileChooserROC.showOpenDialog(m_Self);
					if (retVal != JFileChooser.APPROVE_OPTION)
						return;

					// create plot
					String filename = m_FileChooserROC.getSelectedFile()
							.getAbsolutePath();
					Instances result = null;
					try {
						result = new Instances(new BufferedReader(
								new FileReader(filename)));
					} catch (Exception e) {
						e.printStackTrace();
						JOptionPane.showMessageDialog(m_Self,
								"Error loading file '" + filename + "':\n"
										+ e.getMessage());
						return;
					}
					result.setClassIndex(result.numAttributes() - 1);
					ThresholdVisualizePanel vmc = new ThresholdVisualizePanel();
					vmc.setROCString("(Area under ROC = "
							+ Utils.doubleToString(ThresholdCurve
									.getROCArea(result), 4) + ")");
					vmc.setName(result.relationName());
					PlotData2D tempd = new PlotData2D(result);
					tempd.setPlotName(result.relationName());
					tempd.addInstanceNumberAttribute();
					try {
						vmc.addPlot(tempd);
					} catch (Exception e) {
						e.printStackTrace();
						JOptionPane.showMessageDialog(m_Self,
								"Error adding plot:\n" + e.getMessage());
						return;
					}

					createFrame(m_Self, jMenuItemVisualizationROC.getText()
							+ " - " + filename, vmc, new BorderLayout(),
							BorderLayout.CENTER, 800, 600, null, true, true);
				}
			});

			// Visualization/TreeVisualizer
			jMenuItemVisualizationTreeVisualizer = new JMenuItem();
			jMenuVisualization.add(jMenuItemVisualizationTreeVisualizer);
			jMenuItemVisualizationTreeVisualizer.setText("TreeVisualizer");
			jMenuItemVisualizationTreeVisualizer.setMnemonic('T');
			jMenuItemVisualizationTreeVisualizer
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							// choose file
							int retVal = m_FileChooserTreeVisualizer
									.showOpenDialog(m_Self);
							if (retVal != JFileChooser.APPROVE_OPTION)
								return;

							// build tree
							String filename = m_FileChooserTreeVisualizer
									.getSelectedFile().getAbsolutePath();
							TreeBuild builder = new TreeBuild();
							Node top = null;
							NodePlace arrange = new PlaceNode2();
							try {
								top = builder.create(new FileReader(filename));
							} catch (Exception e) {
								e.printStackTrace();
								JOptionPane.showMessageDialog(m_Self,
										"Error loading file '" + filename
												+ "':\n" + e.getMessage());
								return;
							}

							// create frame
							createFrame(m_Self,
									jMenuItemVisualizationTreeVisualizer
											.getText()
											+ " - " + filename,
									new TreeVisualizer(null, top, arrange),
									new BorderLayout(), BorderLayout.CENTER,
									800, 600, null, true, true);
						}
					});

			// Visualization/GraphVisualizer
			jMenuItemVisualizationGraphVisualizer = new JMenuItem();
			jMenuVisualization.add(jMenuItemVisualizationGraphVisualizer);
			jMenuItemVisualizationGraphVisualizer.setText("GraphVisualizer");
			jMenuItemVisualizationGraphVisualizer.setMnemonic('G');
			jMenuItemVisualizationGraphVisualizer
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							// choose file
							int retVal = m_FileChooserGraphVisualizer
									.showOpenDialog(m_Self);
							if (retVal != JFileChooser.APPROVE_OPTION)
								return;

							// build graph
							String filename = m_FileChooserGraphVisualizer
									.getSelectedFile().getAbsolutePath();
							GraphVisualizer panel = new GraphVisualizer();
							try {
								if (filename.toLowerCase().endsWith(".xml")
										|| filename.toLowerCase().endsWith(
												".bif")) {
									panel
											.readBIF(new FileInputStream(
													filename));
								} else {
									panel.readDOT(new FileReader(filename));
								}
							} catch (Exception e) {
								e.printStackTrace();
								JOptionPane.showMessageDialog(m_Self,
										"Error loading file '" + filename
												+ "':\n" + e.getMessage());
								return;
							}

							// create frame
							createFrame(m_Self,
									jMenuItemVisualizationGraphVisualizer
											.getText()
											+ " - " + filename, panel,
									new BorderLayout(), BorderLayout.CENTER,
									800, 600, null, true, true);
						}
					});

			// Visualization/BoundaryVisualizer
			jMenuItemVisualizationBoundaryVisualizer = new JMenuItem();
			jMenuVisualization.add(jMenuItemVisualizationBoundaryVisualizer);
			jMenuItemVisualizationBoundaryVisualizer
					.setText("BoundaryVisualizer");
			jMenuItemVisualizationBoundaryVisualizer.setMnemonic('B');
			jMenuItemVisualizationBoundaryVisualizer
					.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent evt) {
							String title = jMenuItemVisualizationBoundaryVisualizer
									.getText();
							if (!containsWindow(title)) {
								createFrame(m_Self, title,
										new BoundaryVisualizer(),
										new BorderLayout(),
										BorderLayout.CENTER, 800, 600, null,
										true, true);
								// dont' do a System.exit after last window got
								// closed!
								BoundaryVisualizer
										.setExitIfNoWindowsOpen(false);
							} else {
								showWindow(getWindow(title));
							}
						}
					});

			// Extensions
			jMenuExtensions = new JMenu("Extensions");
			jMenuExtensions.setMnemonic(java.awt.event.KeyEvent.VK_E);
			jMenuBar.add(jMenuExtensions);
			jMenuExtensions.setVisible(false);

			String extensions = GenericObjectEditor.EDITOR_PROPERTIES
					.getProperty(MainMenuExtension.class.getName(), "");

			if (extensions.length() > 0) {
				jMenuExtensions.setVisible(true);
				String[] classnames = GenericObjectEditor.EDITOR_PROPERTIES
						.getProperty(MainMenuExtension.class.getName(), "")
						.split(",");
				Hashtable<String, JMenu> submenus = new Hashtable<String, JMenu>();

				// add all extensions
				for (int i = 0; i < classnames.length; i++) {
					String classname = classnames[i];
					try {
						MainMenuExtension ext = (MainMenuExtension) Class
								.forName(classname).newInstance();

						// menuitem in a submenu?
						JMenu submenu = null;
						if (ext.getSubmenuTitle() != null) {
							submenu = submenus.get(ext.getSubmenuTitle());
							if (submenu == null) {
								submenu = new JMenu(ext.getSubmenuTitle());
								submenus.put(ext.getSubmenuTitle(), submenu);
								insertMenuItem(jMenuExtensions, submenu);
							}
						}

						// create menu item
						JMenuItem menuitem = new JMenuItem();
						menuitem.setText(ext.getMenuTitle());
						// does the extension need a frame or does it have its
						// own ActionListener?
						ActionListener listener = ext.getActionListener(m_Self);
						if (listener != null) {
							menuitem.addActionListener(listener);
						} else {
							final JMenuItem finalMenuitem = menuitem;
							final MainMenuExtension finalExt = ext;
							menuitem.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									Component frame = createFrame(m_Self,
											finalMenuitem.getText(), null,
											null, null, -1, -1, null, false,
											false);
									finalExt.fillFrame(frame);
									frame.setVisible(true);
								}
							});
						}

						// sorted insert of menu item
						if (submenu != null)
							insertMenuItem(submenu, menuitem);
						else
							insertMenuItem(jMenuExtensions, menuitem);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}

			// Windows
			jMenuWindows = new JMenu("Windows");
			jMenuWindows.setMnemonic(java.awt.event.KeyEvent.VK_W);
			jMenuBar.add(jMenuWindows);
			jMenuWindows.setVisible(false); // initially, there are no windows
											// open

			// Help
			jMenuHelp = new JMenu();
			jMenuBar.add(jMenuHelp);
			jMenuHelp.setText("Help");
			jMenuHelp.setMnemonic('H');

			// Help/Homepage
			jMenuItemHelpHomepage = new JMenuItem();
			jMenuHelp.add(jMenuItemHelpHomepage);
			jMenuItemHelpHomepage.setText("Weka homepage");
			jMenuItemHelpHomepage.setMnemonic('H');
			jMenuItemHelpHomepage.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					BrowserHelper.openURL(m_Self,
							"http://www.cs.waikato.ac.nz/~ml/weka/");
				}
			});

			jMenuHelp.add(new JSeparator());

			/*
			 * // Help/WekaDoc jMenuItemHelpWekaDoc = new JMenuItem();
			 * jMenuHelp.add(jMenuItemHelpWekaDoc);
			 * jMenuItemHelpWekaDoc.setText("Online documentation");
			 * jMenuItemHelpWekaDoc.setMnemonic('D');
			 * jMenuItemHelpWekaDoc.addActionListener(new ActionListener() {
			 * public void actionPerformed(ActionEvent evt) {
			 * BrowserHelper.openURL(m_Self,
			 * "http://weka.sourceforge.net/wekadoc/"); } });
			 */

			// Help/WekaWiki
			jMenuItemHelpWekaWiki = new JMenuItem();
			jMenuHelp.add(jMenuItemHelpWekaWiki);
			jMenuItemHelpWekaWiki.setText("HOWTOs, code snippets, etc.");
			jMenuItemHelpWekaWiki.setMnemonic('W');
			jMenuItemHelpWekaWiki.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					BrowserHelper.openURL(m_Self,
							"http://weka.wiki.sourceforge.net/");
				}
			});

			// Help/Sourceforge
			jMenuItemHelpSourceforge = new JMenuItem();
			jMenuHelp.add(jMenuItemHelpSourceforge);
			jMenuItemHelpSourceforge.setText("Weka on SourceForge");
			jMenuItemHelpSourceforge.setMnemonic('F');
			jMenuItemHelpSourceforge.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					BrowserHelper.openURL(m_Self,
							"http://sourceforge.net/projects/weka/");
				}
			});

			jMenuHelp.add(new JSeparator());

			// Help/SystemInfo
			jMenuItemHelpSystemInfo = new JMenuItem();
			jMenuHelp.add(jMenuItemHelpSystemInfo);
			jMenuItemHelpSystemInfo.setText("SystemInfo");
			jMenuItemHelpHomepage.setMnemonic('S');
			jMenuItemHelpSystemInfo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					String title = jMenuItemHelpSystemInfo.getText();
					if (!containsWindow(title)) {
						// get info
						Hashtable info = new SystemInfo().getSystemInfo();

						// sort names
						Vector names = new Vector();
						Enumeration enm = info.keys();
						while (enm.hasMoreElements())
							names.add(enm.nextElement());
						Collections.sort(names);

						// generate table
						String[][] data = new String[info.size()][2];
						for (int i = 0; i < names.size(); i++) {
							data[i][0] = names.get(i).toString();
							data[i][1] = info.get(data[i][0]).toString();
						}
						String[] titles = new String[] { "Key", "Value" };
						JTable table = new JTable(data, titles);

						createFrame(m_Self, title, new JScrollPane(table),
								new BorderLayout(), BorderLayout.CENTER, 800,
								600, null, true, true);
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			jMenuHelp.add(new JSeparator());

			// Help/About
			jMenuItemHelpAbout = new JMenuItem();
			jMenuHelp.add(jMenuItemHelpAbout);
			jMenuItemHelpAbout.setText("About");
			jMenuItemHelpAbout.setMnemonic('A');
			jMenuItemHelpAbout.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					String title = jMenuItemHelpAbout.getText();
					if (!containsWindow(title)) {
						JPanel wekaPan = new JPanel();
						wekaPan
								.setToolTipText("Weka, a native bird of New Zealand");
						ImageIcon wii = new ImageIcon(
								Toolkit
										.getDefaultToolkit()
										.getImage(
												ClassLoader
														.getSystemResource("weka/gui/weka3.gif")));
						JLabel wekaLab = new JLabel(wii);
						wekaPan.add(wekaLab);
						Container frame = createFrame(m_Self, title, wekaPan,
								new BorderLayout(), BorderLayout.CENTER, -1,
								-1, null, true, true);

						JPanel titlePan = new JPanel();
						titlePan.setLayout(new GridLayout(8, 1));
						titlePan.setBorder(BorderFactory.createEmptyBorder(10,
								5, 10, 5));
						titlePan.add(new JLabel("Waikato Environment for",
								SwingConstants.CENTER));
						titlePan.add(new JLabel("Knowledge Analysis",
								SwingConstants.CENTER));
						titlePan.add(new JLabel(""));
						titlePan.add(new JLabel("Version " + Version.VERSION,
								SwingConstants.CENTER));
						titlePan.add(new JLabel(""));
						titlePan
								.add(new JLabel("(c) "
										+ Copyright.getFromYear() + " - "
										+ Copyright.getToYear(),
										SwingConstants.CENTER));
						titlePan.add(new JLabel(Copyright.getOwner(),
								SwingConstants.CENTER));
						titlePan.add(new JLabel(Copyright.getAddress(),
								SwingConstants.CENTER));

						if (frame instanceof ChildFrameMDI) {
							((ChildFrameMDI) frame).getContentPane().add(
									titlePan, BorderLayout.NORTH);
							((ChildFrameMDI) frame).pack();
						} else if (frame instanceof ChildFrameSDI) {
							((ChildFrameSDI) frame).getContentPane().add(
									titlePan, BorderLayout.NORTH);
							((ChildFrameSDI) frame).pack();
						}
					} else {
						showWindow(getWindow(title));
					}
				}
			});

			// size + position
			int screenHeight = getGraphicsConfiguration().getBounds().height;
			int screenWidth = getGraphicsConfiguration().getBounds().width;
			if (m_GUIType == GUI_MDI) {
				int newHeight = (int) (((double) screenHeight) * 0.75);
				int newWidth = (int) (((double) screenWidth) * 0.75);
				setSize(1000 > newWidth ? newWidth : 1000,
						800 > newHeight ? newHeight : 800);
				setLocation((screenWidth - getBounds().width) / 2,
						(screenHeight - getBounds().height) / 2);
			} else if (m_GUIType == GUI_SDI) {
				pack();
				setSize(screenWidth, getHeight());
				setLocation(0, 0);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * creates and displays the title.
	 * 
	 * @param title
	 *            the additional part of the title
	 */
	protected void createTitle(String title) {
		String newTitle;

		newTitle = "Weka " + new Version();
		if (title.length() != 0)
			newTitle += " - " + title;

		setTitle(newTitle);
	}

	/**
	 * adds the given child frame to the list of frames.
	 * 
	 * @param c
	 *            the child frame to add
	 */
	public void addChildFrame(Container c) {
		m_ChildFrames.add(c);
		windowListChanged();
	}

	/**
	 * tries to remove the child frame, it returns true if it could do such.
	 * 
	 * @param c
	 *            the child frame to remove
	 * @return true if the child frame could be removed
	 */
	public boolean removeChildFrame(Container c) {
		boolean result = m_ChildFrames.remove(c);
		windowListChanged();
		return result;
	}

	/**
	 * brings child frame to the top.
	 * 
	 * @param c
	 *            the frame to activate
	 * @return true if frame was activated
	 */
	public boolean showWindow(Container c) {
		boolean result;
		ChildFrameMDI mdiFrame;
		ChildFrameSDI sdiFrame;

		if (c != null) {
			try {
				if (c instanceof ChildFrameMDI) {
					mdiFrame = (ChildFrameMDI) c;
					mdiFrame.setIcon(false);
					mdiFrame.toFront();
					createTitle(mdiFrame.getTitle());
				} else if (c instanceof ChildFrameSDI) {
					sdiFrame = (ChildFrameSDI) c;
					sdiFrame.setExtendedState(JFrame.NORMAL);
					sdiFrame.toFront();
					createTitle(sdiFrame.getTitle());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			result = true;
		} else {
			result = false;
		}

		return result;
	}

	/**
	 * brings the first frame to the top that is of the specified window class.
	 * 
	 * @param windowClass
	 *            the class to display the first child for
	 * @return true, if a child was found and brought to front
	 */
	public boolean showWindow(Class windowClass) {
		return showWindow(getWindow(windowClass));
	}

	/**
	 * returns all currently open frames.
	 * 
	 * @return an iterator over all currently open frame
	 */
	public Iterator getWindowList() {
		return m_ChildFrames.iterator();
	}

	/**
	 * returns the first instance of the given window class, null if none can be
	 * found.
	 * 
	 * @param windowClass
	 *            the class to retrieve the first instance for
	 * @return null, if no instance can be found
	 */
	public Container getWindow(Class windowClass) {
		Container result;
		Iterator iter;
		Container current;

		result = null;
		iter = getWindowList();
		while (iter.hasNext()) {
			current = (Container) iter.next();
			if (current.getClass() == windowClass) {
				result = current;
				break;
			}
		}

		return result;
	}

	/**
	 * returns the first window with the given title, null if none can be found.
	 * 
	 * @param title
	 *            the title to look for
	 * @return null, if no instance can be found
	 */
	public Container getWindow(String title) {
		Container result;
		Iterator iter;
		Container current;
		boolean found;

		result = null;
		iter = getWindowList();
		while (iter.hasNext()) {
			current = (Container) iter.next();
			found = false;

			if (current instanceof ChildFrameMDI)
				found = ((ChildFrameMDI) current).getTitle().equals(title);
			else if (current instanceof ChildFrameSDI)
				found = ((ChildFrameSDI) current).getTitle().equals(title);

			if (found) {
				result = current;
				break;
			}
		}

		return result;
	}

	/**
	 * checks, whether an instance of the given window class is already in the
	 * Window list.
	 * 
	 * @param windowClass
	 *            the class to check for an instance in the current window list
	 * @return true if the class is already listed in the Window list
	 */
	public boolean containsWindow(Class windowClass) {
		return (getWindow(windowClass) != null);
	}

	/**
	 * checks, whether a window with the given title is already in the Window
	 * list.
	 * 
	 * @param title
	 *            the title to check for in the current window list
	 * @return true if a window with the given title is already listed in the
	 *         Window list
	 */
	public boolean containsWindow(String title) {
		return (getWindow(title) != null);
	}

	/**
	 * minimizes all windows.
	 */
	public void minimizeWindows() {
		Iterator iter;
		Container frame;

		iter = getWindowList();
		while (iter.hasNext()) {
			frame = (Container) iter.next();
			try {
				if (frame instanceof ChildFrameMDI)
					((ChildFrameMDI) frame).setIcon(true);
				else if (frame instanceof ChildFrameSDI)
					((ChildFrameSDI) frame).setExtendedState(JFrame.ICONIFIED);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * restores all windows.
	 */
	public void restoreWindows() {
		Iterator iter;
		Container frame;

		iter = getWindowList();
		while (iter.hasNext()) {
			frame = (Container) iter.next();
			try {
				if (frame instanceof ChildFrameMDI)
					((ChildFrameMDI) frame).setIcon(false);
				else if (frame instanceof ChildFrameSDI)
					((ChildFrameSDI) frame).setExtendedState(JFrame.NORMAL);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * is called when window list changed somehow (add or remove).
	 */
	public void windowListChanged() {
		createWindowMenu();
	}

	/**
	 * creates the menu of currently open windows.
	 */
	protected synchronized void createWindowMenu() {
		Iterator iter;
		JMenuItem menuItem;
		int startIndex;

		// remove all existing entries
		jMenuWindows.removeAll();

		// minimize + restore + separator
		menuItem = new JMenuItem("Minimize");
		menuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				minimizeWindows();
			}
		});
		jMenuWindows.add(menuItem);

		menuItem = new JMenuItem("Restore");
		menuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				restoreWindows();
			}
		});
		jMenuWindows.add(menuItem);

		jMenuWindows.addSeparator();

		// windows
		startIndex = jMenuWindows.getMenuComponentCount() - 1;
		iter = getWindowList();
		jMenuWindows.setVisible(iter.hasNext());
		while (iter.hasNext()) {
			Container frame = (Container) iter.next();
			if (frame instanceof ChildFrameMDI)
				menuItem = new JMenuItem(((ChildFrameMDI) frame).getTitle());
			else if (frame instanceof ChildFrameSDI)
				menuItem = new JMenuItem(((ChildFrameSDI) frame).getTitle());
			insertMenuItem(jMenuWindows, menuItem, startIndex);
			menuItem.setActionCommand(Integer.toString(frame.hashCode()));
			menuItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					Container frame = null;
					Iterator iter = getWindowList();
					while (iter.hasNext()) {
						frame = (Container) iter.next();
						String hashFrame = Integer.toString(frame.hashCode());
						if (hashFrame.equals(evt.getActionCommand())) {
							showWindow(frame);
							break;
						}
					}
					showWindow(frame);
				}
			});
		}
	}

	/**
	 * Shows or hides this component depending on the value of parameter b.
	 * 
	 * @param b
	 *            if true, shows this component; otherwise, hides this component
	 */
	public void setVisible(boolean b) {
		super.setVisible(b);

		if (b)
			paint(this.getGraphics());
	}

	/**
	 * Create the singleton instance of the Main GUI.
	 * 
	 * @param args
	 *            commandline options
	 */
	public static void createSingleton(String[] args) {
		if (m_MainSingleton == null)
			m_MainSingleton = new Main();

		// set options
		try {
			m_MainSingleton.setOptions(args);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// notify listeners (if any)
		for (int i = 0; i < m_StartupListeners.size(); i++)
			((StartUpListener) m_StartupListeners.elementAt(i))
					.startUpComplete();
	}

	/**
	 * Return the singleton instance of the Main GUI.
	 * 
	 * @return the singleton instance
	 */
	public static Main getSingleton() {
		return m_MainSingleton;
	}

	/**
	 * Add a listener to be notified when startup is complete.
	 * 
	 * @param s
	 *            a listener to add
	 */
	public static void addStartupListener(StartUpListener s) {
		m_StartupListeners.add(s);
	}

	/**
	 * Gets an enumeration describing the available options.
	 * 
	 * @return an enumeration of all the available options.
	 */
	public Enumeration listOptions() {
		Vector result;
		String desc;
		SelectedTag tag;
		int i;

		result = new Vector();

		desc = "";
		for (i = 0; i < TAGS_GUI.length; i++) {
			tag = new SelectedTag(TAGS_GUI[i].getID(), TAGS_GUI);
			desc += "\t" + tag.getSelectedTag().getIDStr() + " = "
					+ tag.getSelectedTag().getReadable() + "\n";
		}
		result.addElement(new Option("\tDetermines the layout of the GUI:\n"
				+ desc + "\t(default: " + new SelectedTag(GUI_MDI, TAGS_GUI)
				+ ")", "gui", 1, "-gui " + Tag.toOptionList(TAGS_GUI)));

		return result.elements();
	}

	/**
	 * returns the options of the current setup.
	 * 
	 * @return the current options
	 */
	public String[] getOptions() {
		Vector<String> result;

		result = new Vector();

		result.add("-gui");
		result.add("" + getGUIType());

		return result.toArray(new String[result.size()]);
	}

	/**
	 * Parses the options for this object. <p/>
	 * 
	 * <!-- options-start --> Valid options are: <p/>
	 * 
	 * <pre>
	 *  -gui &lt;MDI|SDI&gt;
	 *  Determines the layout of the GUI:
	 *  MDI = MDI Layout
	 *  SDI = SDI Layout
	 *  (default: MDI)
	 * </pre>
	 * 
	 * <!-- options-end -->
	 * 
	 * @param options
	 *            the options to use
	 * @throws Exception
	 *             if setting of options fails
	 */
	public void setOptions(String[] options) throws Exception {
		String tmpStr;

		tmpStr = Utils.getOption("gui", options);
		if (tmpStr.length() != 0)
			setGUIType(new SelectedTag(tmpStr, TAGS_GUI));
		else
			setGUIType(new SelectedTag(GUI_MDI, TAGS_GUI));
	}

	/**
	 * Sets the type of GUI to use.
	 * 
	 * @param value
	 *            .the GUI type
	 */
	public void setGUIType(SelectedTag value) {
		if (value.getTags() == TAGS_GUI) {
			m_GUIType = value.getSelectedTag().getID();
			initGUI();
		}
	}

	/**
	 * Gets the currently set type of GUI to display.
	 * 
	 * @return the current GUI Type.
	 */
	public SelectedTag getGUIType() {
		return new SelectedTag(m_GUIType, TAGS_GUI);
	}

	/**
	 * starts the application.
	 * 
	 * @param args
	 *            the commandline arguments - ignored
	 */
	public static void main(String[] args) {
		weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO,
				"Logging started");

		LookAndFeel.setLookAndFeel();

		try {
			// uncomment the following line to disable the memory management:
			// m_Memory.setEnabled(false);

			// help?
			if (Utils.getFlag('h', args)) {
				System.out.println();
				System.out.println("Help requested.");
				System.out.println();
				System.out.println("General options:");
				System.out.println();
				System.out.println("-h");
				System.out.println("\tprints this help screen");
				System.out.println();

				Enumeration enu = new Main().listOptions();
				while (enu.hasMoreElements()) {
					Option option = (Option) enu.nextElement();
					System.out.println(option.synopsis());
					System.out.println(option.description());
				}

				System.out.println();
				System.exit(0);
			}

			// setup splash screen
			Main.addStartupListener(new weka.gui.beans.StartUpListener() {
				public void startUpComplete() {
					m_MainCommandline = Main.getSingleton();
					m_MainCommandline.setVisible(true);
				}
			});
			Main.addStartupListener(new StartUpListener() {
				public void startUpComplete() {
					SplashWindow.disposeSplash();
				}
			});
			SplashWindow.splash(ClassLoader
					.getSystemResource("weka/gui/images/weka_splash.gif"));

			// start GUI
			final String[] options = (String[]) args.clone();
			Thread nt = new Thread() {
				public void run() {
					weka.gui.SplashWindow.invokeMethod(Main.class.getName(),
							"createSingleton", options);
				}
			};
			nt.start();

			Thread memMonitor = new Thread() {
				public void run() {
					while (true) {
						try {
							Thread.sleep(4000);
							System.gc();

							if (m_Memory.isOutOfMemory()) {
								// clean up
								m_MainCommandline = null;
								System.gc();

								// stop threads
								m_Memory.stopThreads();

								// display error
								System.err.println("\ndisplayed message:");
								m_Memory.showOutOfMemory();
								System.err.println("\nexiting");
								System.exit(-1);
							}

						} catch (InterruptedException ex) {
							ex.printStackTrace();
						}
					}
				}
			};

			memMonitor.setPriority(Thread.MAX_PRIORITY);
			memMonitor.start();
		} catch (Exception ex) {
			ex.printStackTrace();
			System.err.println(ex.getMessage());
		}
	}
}
